/*
 * Copyright (c) 2007 Embedded Projects (http://www.embedded-projects.net)
 * Author: Benedikt Sauter <sauter@embedded-projects.net>
 * All rights reserved.
 *
 * Short descripton of file:
 *
 *
 * Redistribution and use in source and binary forms, with or without 
 * modification, are permitted provided that the following conditions 
 * are met:
 *
 *   * Redistributions of source code must retain the above copyright 
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above 
 *     copyright notice, this list of conditions and the following 
 *     disclaimer in the documentation and/or other materials provided 
 *     with the distribution.
 *   * Neither the name of the FH Augsburg nor the names of its 
 *     contributors may be used to endorse or promote products derived 
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES{} LOSS OF USE, 
 * DATA, OR PROFITS{} OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "common.h"
#include "protocol.h"
#include "io.h"
#include "pin.h"


void io_parser(char *buf)
{
   switch(buf[0])
   {
      case CMD_IO_INIT_PORT:
         io_init_port_usb((uint8_t)buf[2]);
      break;
      case CMD_IO_INIT_PIN:
         io_init_usb((uint8_t)buf[2]);
      break;
      case CMD_IO_PORT_DIRECTION_IN:
         io_set_port_direction_in_usb((uint8_t)buf[2],(uint8_t)buf[3]);
      break;
      case CMD_IO_PORT_DIRECTION_OUT:
         io_set_port_direction_out_usb((uint8_t)buf[2],(uint8_t)buf[3]);
      break;
      case CMD_IO_PORT_DIRECTION_TRI:
         io_set_port_direction_tri_usb((uint8_t)buf[2],(uint8_t)buf[3]);
      break;
      case CMD_IO_PIN_DIRECTION_IN:
         io_set_pin_direction_in_usb((uint8_t)buf[2]);
      break;
      case CMD_IO_PIN_DIRECTION_OUT:
         io_set_pin_direction_out_usb((uint8_t)buf[2]);
      break;
      case CMD_IO_PIN_DIRECTION_TRI:
         io_set_pin_direction_tri_usb((uint8_t)buf[2]);
      break;
      case CMD_IO_PORT_SET:
         io_set_port_usb((uint8_t)buf[2],(uint8_t)buf[3]);
      break;
      case CMD_IO_PORT_GET:
         io_get_port_usb((uint8_t)buf[2]);
      break;
      case CMD_IO_PIN_SET:
         io_set_pin_usb((uint8_t)buf[2],(uint8_t)buf[3]);
      break;
      case CMD_IO_PIN_GET:
         io_get_pin_usb((uint8_t)buf[2]);
      break;
      case CMD_IO_SET_STATUS_LED:
         io_set_status_led_usb((uint8_t)buf[2]);
      break;
      default:
         answer[0] = buf[0];
         answer[1] = RSP_UNKOWN_CMD;
         answer[2] = 0;
         CommandAnswer(3);
   }
}

uint8_t io_init(uint8_t pin) 
{
  return io_set_pin_direction_out(pin);
}

void io_init_usb(uint8_t pin)
{
   answer[0] = CMD_IO_INIT_PIN;
   answer[1] = (unsigned char)io_init(pin);
   answer[2] = 0;
   CommandAnswer(3);
}

uint8_t io_init_port(uint8_t port)
{
    /* TODO implement this function */
    return RSP_OK;
}

void io_init_port_usb(uint8_t port)
{
   answer[0] = CMD_IO_INIT_PORT;
   answer[1] = (unsigned char)io_init_port(port);
   answer[2] = 0;
   CommandAnswer(3);
}

uint8_t io_set_port_direction_out(uint8_t port,uint8_t mask)
{
    /* TODO
	switch (port)
	{
      case  1:   DDRB = mask; break;
      case  2:   DDRC = mask; break; // PC7 = /CS (output) - do we need to mask this ?
      case  3:   DDRD = mask; break;
      case  4:   DDRE = mask; break; // PE4 = INT4 (input) - do we need to mask this ?
      case  5:   DDRF = mask; break;
      default:   return RSP_UNKOWN_PIN;
	}
    */
	return RSP_OK;    
}

uint8_t io_set_port_direction_in(uint8_t port,uint8_t mask)
{
    /* TODO implement */
    return RSP_OK;
}

uint8_t io_set_port_direction_tri(uint8_t port,uint8_t mask)
{   
    /* TODO implement */
    return RSP_OK;
}

void io_set_port_direction_out_usb (uint8_t port, uint8_t mask)
{
   answer[0] = CMD_IO_PORT_DIRECTION_OUT;
   answer[1] = (unsigned char)io_set_port_direction_out (port, mask);
   answer[2] = 0;
   CommandAnswer(3);
}

void io_set_port_direction_in_usb(uint8_t port, uint8_t mask)
{
   answer[0] = CMD_IO_PORT_DIRECTION_IN;
   answer[1] = (unsigned char)io_set_port_direction_in (port, mask);
   answer[2] = 0;
   CommandAnswer(3);
}

void io_set_port_direction_tri_usb(uint8_t port, uint8_t mask)
{
   answer[0] = CMD_IO_PORT_DIRECTION_TRI;
   answer[1] = (unsigned char)io_set_port_direction_tri (port, mask);
   answer[2] = 0;
   CommandAnswer(3);
}


uint8_t io_set_pin_direction_out(uint8_t pin)
{
  pin_conf p;

  if(!pin_get_conf(pin, &p))
    return RSP_UNKOWN_PIN;
  
  octopus.ports[pin] = PIN_OUT;
  *(p.ox_ddr) |= (1 << p.ox_p);

  return RSP_OK;
}

uint8_t io_set_pin_direction_in(uint8_t  pin)
{
  pin_conf p;

  if(!pin_get_conf(pin, &p))
    return RSP_UNKOWN_PIN;
  
  octopus.ports[pin] = PIN_IN;
  *(p.ox_ddr) &= ~(1 << p.ox_p);

  return RSP_OK;
}


uint8_t io_set_pin_direction_tri(uint8_t pin)
{
   if(io_set_pin_direction_out(pin) == RSP_OK && 
      io_set_pin(pin,1)             == RSP_OK)
   {
     octopus.ports[pin] = PIN_TRI;
     return RSP_OK;
   }
   else 
     return RSP_UNKOWN_PIN;
}


void io_set_pin_direction_out_usb(uint8_t  pin)
{
   answer[0] = CMD_IO_PIN_DIRECTION_OUT;
   answer[1] = (unsigned char)io_set_pin_direction_out(pin);
   answer[2] = 0;  
   CommandAnswer(3);
}

void io_set_pin_direction_in_usb(uint8_t  pin)
{
   answer[0] = CMD_IO_PIN_DIRECTION_IN;
   answer[1] = (unsigned char)io_set_pin_direction_in(pin);
   answer[2] = 0;  
   CommandAnswer(3);
}

void io_set_pin_direction_tri_usb(uint8_t  pin)
{
   answer[0] = CMD_IO_PIN_DIRECTION_TRI;
   answer[1] = (unsigned char)io_set_pin_direction_tri(pin);
   answer[2] = 0;  
   CommandAnswer(3);
}


//XXX
uint8_t io_get_port (uint8_t port, uint8_t *pvalue)
{
  uint8_t value;

  *pvalue = 0;
  switch (port)
  {
      case  1:   value = PINB; break;
      case  2:   value = PINC; break; /* PC7 = /CS */
      case  3:   value = PIND; break;
      case  4:   value = PINE; break; /* PE4 = INTR */
      case  5:   value = PINF; break;
      default:   return RSP_UNKOWN_PORT;
  }

  *pvalue = value;
  return RSP_OK;
}

void io_get_port_usb (uint8_t port)
{
	uint8_t value;

    answer[0] = CMD_IO_PORT_GET;
	answer[1] = (unsigned char)io_get_port (port, &value);
	answer[2] = (unsigned char)value;
        answer[3] = 0;
	CommandAnswer(4);
}

// XXX
uint8_t io_set_port(uint8_t port, uint8_t value)
{
	switch (port)
	{
      case  1:   PORTB = value; break;
      case  2:   PORTC = value; break; // PC7 = /CS
      case  3:   PORTD = value; break;
      case  4:   PORTE = value; break; // PE4 = INTR 
      case  5:   PORTF = value; break;
      default:   return RSP_UNKOWN_PIN;
	}
	return RSP_OK;
}

void io_set_port_usb (uint8_t port, uint8_t value)
{
	answer[0] = CMD_IO_PORT_SET;
	answer[1] = (unsigned char)io_set_port (port, value);
	answer[2] = 0;
	CommandAnswer(3);
}


void io_set_pin_usb(uint8_t pin, uint8_t value)
{
   answer[0] = CMD_IO_PIN_SET;
   answer[1] = (unsigned char)io_set_pin(pin,value);
   answer[2] = 0;
   CommandAnswer(3);
}


uint8_t io_set_pin(uint8_t pin, uint8_t value)
{
  pin_conf p;

  if(!pin_get_conf(pin, &p))
    return RSP_UNKOWN_PIN;
  
  if(octopus.ports[pin] != PIN_OUT)
    return RSP_WRONG_PIN_CONFIG;

  if(value)
    *(p.ox_port) |=  (1 << p.ox_p);
  else
    *(p.ox_port) &= ~(1 << p.ox_p);

  return RSP_OK;
}


void io_get_pin_usb(uint8_t pin)
{
   uint8_t value = 0;
   
   answer[0] = CMD_IO_PIN_GET;
   answer[1] = (unsigned char)io_get_pin(pin,&value);  
   answer[2] = (unsigned char)value;  
   answer[3] = 0;
   CommandAnswer(4);
}

uint8_t io_get_pin(uint8_t pin, uint8_t *value)
{
  pin_conf p;

  if(!pin_get_conf(pin, &p))
    return RSP_UNKOWN_PIN;

  if(octopus.ports[pin] != PIN_IN)
    return RSP_WRONG_PIN_CONFIG;

  if(*(p.ox_pin) & (1 << p.ox_p))
    *value = 1;
  else
    *value = 0;

  return RSP_OK;
}

void io_set_status_led(uint8_t on)
{
   if(on)
     octopus.status_led = 1;
   else
     octopus.status_led = 0;
}

void io_set_status_led_usb(uint8_t on)
{
   io_set_status_led(on);
   
   answer[0] = CMD_IO_PIN_GET;
   answer[1] = on;
   answer[2] = 0;
   CommandAnswer(3);
}
